package com.javasee.aiqueue;

import com.google.common.base.Charsets;
import com.javasee.common.module.ModuleEnableConst;
import lombok.Data;
import lombok.ToString;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import sun.nio.ch.DirectBuffer;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Method;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * index文件管理
 * @author : xinyanfei
 * @date : 2025-08-27 15:51:11
 * Copyright (c) , 京东商城  All Rights Reserved.
 *
 *
 *
 *  ai_i（头） 占4位 +  版本 占4位 + 文件大小占4位 + 写索引文件编号占4位 + 写偏移量占4位 + 读索引文件编号占4位 + 读偏移量占4位 + 写次数占8位+读次数占8位
 *
 *     4                  4               4                4              4              4              4             8       8
 *
 *     28+16 = 44
 */

@Service
@Profile(ModuleEnableConst.AIQUEUE)
public class AiIndexFileService {

    //文件头
    byte[] header = "ai_i".getBytes(Charsets.US_ASCII);
    // 版本
    int VERSION_1 = 1;
    //每个文件的大小10M
    int maxPerFile = 1024*1024*10;

    int MIN_HEADER_SIZE = 12;
    long IDX_SIZE = 44L;


    private MappedByteBuffer indexMappedByteBuffer;

    String indexFile;

    int writeIndex = 0;
    int writeOffset = 0;
    int readIndex = 0;
    int readOffset = 0;
    final AtomicInteger writeCount = new AtomicInteger(0);
    long writeCount2 = 0;
    long readCount = 0;


    public AiIndexFileService(AiQueueProperties aiQueueProperties) throws IOException {

        System.out.println("===================初始化index.idx文件 或者 读取偏移量数据 开始===================");
        //index.idx
        indexFile = String.format("%s%sindex.idx", aiQueueProperties.getFileHome() , File.separator);
        File fileIdx = new File(indexFile);
        boolean newFile = false;
        if (!fileIdx.exists()) {
            newFile = fileIdx.createNewFile();
        }
        RandomAccessFile indexFile = new RandomAccessFile(fileIdx, "rwd");

        try {
            indexMappedByteBuffer = indexFile.getChannel().map(FileChannel.MapMode.READ_WRITE,0, IDX_SIZE);
            indexMappedByteBuffer.order(ByteOrder.BIG_ENDIAN);

            if(newFile) {
                initHeader();
                //writeIndexContext(indexMappedByteBuffer);
            }else{
                //检查
                if(checkHeader(indexMappedByteBuffer)){
                    System.out.println("每次启动aiqueue时，header 完成验证");
                }
                //读取数据
                readIndexContext(indexMappedByteBuffer);
                System.out.println("maxPerFile = " + maxPerFile);
                System.out.println("writeIndex = " + writeIndex);
                System.out.println("writeOffset = " + writeOffset);
                System.out.println("readIndex = " + readIndex);
                System.out.println("readOffset = " + readOffset);
                System.out.println("writeCount2 = " + writeCount2);
                System.out.println("readCount = " + readCount);
                System.out.println("完成读取index.idx偏移量等数据");
            }
            System.out.println("===================初始化index.idx文件 或者 读取偏移量数据 结束===================");
        } catch (IOException e) {
            free(indexMappedByteBuffer);
            throw e;
        } finally {
            indexFile.close();
        }
    }

    private void readIndexContext(MappedByteBuffer idxMbb) {
        this.maxPerFile = idxMbb.getInt();
        this.writeIndex = idxMbb.getInt();
        this.writeOffset = idxMbb.getInt();
        this.readIndex = idxMbb.getInt();
        this.readOffset = idxMbb.getInt();
        this.writeCount2 = idxMbb.getLong();
        this.readCount = idxMbb.getLong();
    }

    private void writeIndexContext(MappedByteBuffer idxMbb) {
        idxMbb.position(MIN_HEADER_SIZE);
        idxMbb.putInt(writeIndex);
        idxMbb.putInt(writeOffset);
        idxMbb.putInt(readIndex);
        idxMbb.putInt(readOffset);
        idxMbb.putLong(writeCount2);
        idxMbb.putLong(readCount);
    }

    /**
     * 头 + version
     * @param idxMbb
     * @return
     */
    private boolean checkHeader(MappedByteBuffer idxMbb) {
        byte[] header1 = new byte[4];
        idxMbb.get(header1);
        int version = idxMbb.getInt();
        if(Arrays.equals(this.header,header1) && VERSION_1 == version) {
            return true;
        }
        return false;
    }

    @PostConstruct
    public void init() throws IOException {
        // Initialize write count from existing index file
//        if (indexFile.length() > 0) {
//            indexFile.seek(indexFile.length() - 1);
//            // Find the last newline character
//            long pos = indexFile.getFilePointer();
//            while (pos >= 0) {
//                indexFile.seek(pos);
//                char c = (char) indexFile.readByte();
//                if (c == '\n') {
//                    String lastLine = indexFile.readLine();
//                    if (lastLine != null && !lastLine.isEmpty()) {
//                        String[] parts = lastLine.split(",");
//                        writeCount.set(Integer.parseInt(parts[0]));
//                    }
//                    break;
//                }
//                pos--;
//            }
//        }

//        indexMappedByteBuffer.order(ByteOrder.BIG_ENDIAN);
//
//        if (idxFile.length() > 12) {
//            if (checkHeader()){
//                System.out.println("header 完成验证");
//            }else{
//                initHeader();
//            }
//        }else {
//            initHeader();
//        }
    }

    void initHeader() throws IOException {
        System.out.println("第一次使用aiqueue，初始化index.idx 文件header开始");
        indexMappedByteBuffer.position(0);
        indexMappedByteBuffer.put(header);
        indexMappedByteBuffer.putInt(this.VERSION_1);
        indexMappedByteBuffer.putInt(this.maxPerFile);
        System.out.println("第一次使用aiqueue，初始化index.idx 文件header完成");
    }


    public synchronized int addIndexEntry(long offset, int length) throws IOException {
//        int count = writeCount.incrementAndGet();
//        String entry = String.format("%d,%d,%d\n", count, offset, length);
//        indexFile.seek(indexFile.length());
//        indexFile.write(entry.getBytes(StandardCharsets.UTF_8));
//        return count;
        return 0;
    }

    public void close() throws IOException {
//        indexFile.close();
    }

    public AiQueueIndex readIndexOffset() {

        if(!canRead()){
            return null;
        }

        if(this.readOffset==0){
            System.out.println("第一次读 或 重新映射map readOffset = " + readOffset);
            //indexMappedByteBuffer.position( 24);
            //this.readOffset = indexMappedByteBuffer.getInt();
        }
        //offset==0 说明没有读取过
        return new AiQueueIndex(readOffset,readOffset);
    }

    public void updateReadIndex(AiQueueIndex index) {
        //参考上面的文件结构
//        indexMappedByteBuffer.position(MIN_HEADER_SIZE + 12);
//        //上一次读索引偏移量
//        int lastReadIndexOffset = indexMappedByteBuffer.getInt();
//
//        indexMappedByteBuffer.position(MIN_HEADER_SIZE + 12);
//        indexMappedByteBuffer.putInt(lastReadIndexOffset + index.offset + index.length);
//        indexMappedByteBuffer.force();
    }

    public void updateWriteIndex(int offset) {
        indexMappedByteBuffer.position(MIN_HEADER_SIZE + 4);
        indexMappedByteBuffer.putInt(offset);
        if(offset!=writeOffset){
            writeOffset = offset;
        }
    }

    void incrementWriteOffset(int incr) {
        synchronized (this) {
            writeOffset += incr;
            writeCount2++;
        }
//        if (!preallocNeeded && writeOffset > maxBytesPerFile / 2) {
//            preallocNeeded = true;
//        }
    }
    void sync() {
        writeStatus(indexMappedByteBuffer);
        indexMappedByteBuffer.force();
    }

    private void writeStatus(MappedByteBuffer mmf) {
//        byte[] block = new byte[32];
//        ByteBuffer buf = ByteBuffer.wrap(block);
//        synchronized (this) {
//            buf.putInt(writeIndex);
//            buf.putInt(writeOffset);
//            buf.putInt(readIndex);
//            buf.putInt(readOffset);
//            buf.putLong(writeCount2);
//            buf.putLong(readCount);
//        }
//        mmf.position(12);
//        mmf.put(block); // atomic write
        synchronized (this) {
            try {
                indexMappedByteBuffer.position(12);
                if(indexMappedByteBuffer.position()!=12){
                    System.out.println("position未起作用，需重新映射所需区域 = ");
                    // 取消当前映射
                    ((DirectBuffer)indexMappedByteBuffer).cleaner().clean();

                    System.out.println("取消当前映射 indexMappedByteBuffer = " + indexMappedByteBuffer);
                    RandomAccessFile raf = new RandomAccessFile(indexFile, "rwd");
                    // 重新映射所需区域
                    indexMappedByteBuffer = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, IDX_SIZE);
                    System.out.println("重新映射 indexMappedByteBuffer = " + indexMappedByteBuffer);
                    // 重新映射整个文件（如果需要）
                    //mmf = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, IDX_SIZE);
                }
                indexMappedByteBuffer.position(12);
                indexMappedByteBuffer.putInt(writeIndex);
                indexMappedByteBuffer.putInt(writeOffset);
                indexMappedByteBuffer.putInt(readIndex);
                indexMappedByteBuffer.putInt(readOffset);
                indexMappedByteBuffer.putLong(writeCount2);
                indexMappedByteBuffer.putLong(readCount);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    boolean canRead() {
        if (readIndex < writeIndex) {
            return true;
        }
        synchronized (this) {
            System.out.println("读索引:"+readOffset +" , 写索引："+writeOffset);
            return readIndex < writeIndex || readOffset < writeOffset;
        }
    }

    /**
     * 2025-08-27 21:36:46
     * @return 获取写索引偏移量
     */
    public int getWriteIndexOffset() {
        indexMappedByteBuffer.position(16);
        int writeOffset1 = indexMappedByteBuffer.getInt();
        if(writeOffset1!=writeOffset){
            writeOffset = writeOffset1;
        }
        return writeOffset;
    }

    @Data
    @ToString
    static class AiQueueIndex {
        //偏移
        int offset;
        //数据长度
        int length;
        public AiQueueIndex(int offset, int length) {
            this.offset = offset;
            this.length = length;
        }
    }

    static void free(final MappedByteBuffer mbb) {
        if (mbb == null) {
            return;
        }
        try {
            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
                @SuppressWarnings("restriction")
                public Void run() throws Exception {
                    Method cleanerMethod = mbb.getClass().getMethod("cleaner", new Class[0]);
                    cleanerMethod.setAccessible(true);
                    sun.misc.Cleaner cleaner = (sun.misc.Cleaner) cleanerMethod.invoke(mbb, new Object[0]);
                    cleaner.clean();
                    return null;
                }
            });
        } catch (PrivilegedActionException e) {
            System.out.println("failed free MappedByteBuffer");
        }
    }
}